package com.zheng.study.web.shiro.realm;

import com.zheng.study.base.service.redis.RedisService;
import com.zheng.study.base.utils.LogUtil;
import com.zheng.study.company.entity.Admin;
import com.zheng.study.company.service.AdminService;
import com.zheng.study.web.shiro.Constants;
import com.zheng.study.web.shiro.codec.HmacSHA256Utils;
import com.zheng.study.web.shiro.credentials.LoginHashedCredentialsMatcher;
import com.zheng.study.web.shiro.service.ShiroService;
import com.zheng.study.web.shiro.utils.ShiroRequestUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashMap;
import java.util.Map;

/**
 * newFile
 * ============================================================================
 * author : fallenpanda
 * createDate:  2018/8/17 。
 * ============================================================================
 */
public class UserRealm extends AuthorizingRealm {

	@Autowired
	private LoginHashedCredentialsMatcher loginHashedCredentialsMatcher;
	@Autowired
	private SimpleCredentialsMatcher whiteListCredentialsMatcher;

	@Autowired
	private RedisService redisService;
	@Autowired
	private AdminService adminService;
	@Autowired
	private ShiroService shiroService;


	//授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
		return new SimpleAuthorizationInfo();
	}


	//认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		//账号密码登录
		if((token instanceof UsernamePasswordToken) || (token instanceof StatelessLoginToken)){
			this.setCredentialsMatcher(loginHashedCredentialsMatcher);
			return doGetStatelessLoginTokenInfo(token);
		}

		//无状态的审核验证过程
		if(token instanceof StatelessToken){
			this.setCredentialsMatcher(whiteListCredentialsMatcher);
			return doGetStatelessTokenInfo(token);
		}

		//二维码登录
		if(token instanceof StatelessQRCodeLoginToken){
			this.setCredentialsMatcher(whiteListCredentialsMatcher);
			return doGetStatelessQrcodeLoginTokenInfo(token);
		}

		//无状态的审核验证过程
		if(token instanceof StatelessAuthcToken){
			this.setCredentialsMatcher(whiteListCredentialsMatcher);
			return doGetStatelessAuthcTokenInfo(token);
		}

		if(token instanceof StatelessWhiteListToken){
			this.setCredentialsMatcher(whiteListCredentialsMatcher);
			return doGetStatelessWhiteListTokenInfo(token);
		}

		return null;
	}

	/**
	 * 用户登录验证的 Token
	 */
	private AuthenticationInfo doGetStatelessLoginTokenInfo(AuthenticationToken token) {
		String account = (String) token.getPrincipal(); //登录账号
		Admin admin = adminService.getAdminByAccount(account);
		if(admin == null){
			throw new UnknownAccountException(); //没有找到此账号
		}
		//检查账号状态
		checkAccountState(admin);
		return new SimpleAuthenticationInfo(admin.getId(), admin.getPassword(), ByteSource.Util.bytes(admin.getId() + admin.getSalt()), getName());
	}

	/**
	 * 无状态的审核验证过程
	 */
	protected AuthenticationInfo doGetStatelessTokenInfo(AuthenticationToken token) throws AuthenticationException {
		StatelessToken statelessToken = (StatelessToken) token;
		String username = statelessToken.getUsername();
		Admin admin = adminService.getAdminByAccount(username);
		if (admin == null) {
			throw new UnknownAccountException();//错误的账号
		}
		// 检查账号状态
		checkAccountState(admin);
		String key = admin.getSalt();
		if(key == null){
			throw new UnknownAccountException();//没找到帐号
		}
		String serverDigest = HmacSHA256Utils.digest(key, statelessToken.getParams());
		return new SimpleAuthenticationInfo(username, serverDigest, getName());
	}

	/**
	 * 二维码登录的Token
	 */
	protected AuthenticationInfo doGetStatelessQrcodeLoginTokenInfo(AuthenticationToken token) throws AuthenticationException {
		// uid
		String uid = (String) token.getPrincipal();
		if(StringUtils.isEmpty(uid)){
			throw new AuthenticationException();
		}
		// appId & appUUID & timestamp
		String strParams = redisService.get(uid);
		if(StringUtils.isEmpty(uid)){
			throw new AuthenticationException();
		}
		String[] arrayParams = strParams.split(",");
		String appId = arrayParams[0];
		String appUUID = arrayParams[1];
		String timestamp = arrayParams[2];

		Map<String,String> params = new HashMap<String, String>();
		params.put(Constants.REQUEST_HEADER_APPID, appId);
		params.put(Constants.REQUEST_HEADER_APPUUID, appUUID);
		params.put(Constants.REQUEST_HEADER_TIMESTAMP, timestamp);
		params.put(Constants.REQUEST_HEADER_UID, uid);

		String secret = shiroService.getAppSecretByAppId(appId);
		if(StringUtils.isEmpty(secret)){
			throw new AuthenticationException();
		}

		String userSecret = shiroService.getUserSecretByAdminId(uid);
		if(StringUtils.isEmpty(secret)){
			throw new AuthenticationException();
		}

		// 对param进行字典排序
		params = ShiroRequestUtil.getSortedParam(params);

		// 从服务器获取对应的 userSecret / appId appUUID timestamp uid 进行加密
		String serverUserSign = HmacSHA256Utils.digest(userSecret, params);

		// 然后进行客户端消息摘要和服务器端消息摘要的匹配
		return new SimpleAuthenticationInfo(uid, serverUserSign, getName());
	}

	/**
	 * 用户会话权限的Token，产生401没有授权错误
	 */
	protected AuthenticationInfo doGetStatelessAuthcTokenInfo(AuthenticationToken token) throws AuthenticationException {
		// uid
		String uid = (String) token.getPrincipal();
		if(StringUtils.isEmpty(uid)){
			throw new AuthenticationException();
		}
		// appId & appUUID & timestamp & uid
		Map<String,String> param = ((StatelessAuthcToken) token).getParams();
		if(null == param){
			throw new AuthenticationException();
		}
		String appId = null;
		String appUUID = null;
		String timestamp = null;

		if(param.containsKey(Constants.REQUEST_HEADER_APPID)){
			appId = param.get(Constants.REQUEST_HEADER_APPID);
		}else{
			throw new AuthenticationException();
		}
		if(param.containsKey(Constants.REQUEST_HEADER_APPUUID)){
			appUUID = param.get(Constants.REQUEST_HEADER_APPUUID);
		}else{
			throw new AuthenticationException();
		}
		if(param.containsKey(Constants.REQUEST_HEADER_TIMESTAMP)){
			timestamp = param.get(Constants.REQUEST_HEADER_TIMESTAMP);
		}else{
			throw new AuthenticationException();
		}

		String secret = shiroService.getAppSecretByAppId(appId);
		if(StringUtils.isEmpty(secret)){
			throw new AuthenticationException();
		}

		String userSecret = shiroService.getUserSecretByAdminId(uid);
		if(StringUtils.isEmpty(secret)){
			throw new AuthenticationException();
		}

		// 对param进行字典排序
		param = ShiroRequestUtil.getSortedParam(param);

		// 将 appId & appUUID & timestamp 加入到redis进行校验
		if(!shiroService.compareAndSetAppTimestamp(appId, appUUID, timestamp)){
			throw new AuthenticationException();
		}

		// 从服务器获取对应的 userSecret / appId appUUID timestamp uid 进行加密
		String serverUserSign = HmacSHA256Utils.digest(userSecret, param);

		// 然后进行客户端消息摘要和服务器端消息摘要的匹配
		return new SimpleAuthenticationInfo(uid, serverUserSign, getName());
	}

	/**
	 * 白名单权限的Token
	 */
	protected AuthenticationInfo doGetStatelessWhiteListTokenInfo(AuthenticationToken token) throws AuthenticationException {
		// appId
//        String appId = (String) token.getPrincipal();
//        if(StringUtils.isEmpty(appId)){
//            throw new AuthenticationException();
//        }
		// appId & appUUID & timestamp & uid
		Map<String, String> param = ((StatelessWhiteListToken) token).getParams();
		if(null == param){
			throw new AuthenticationException();
		}
		String appId = null;
		String appUUID = null;
		String timestamp = null;

		if(param.containsKey(Constants.REQUEST_HEADER_APPID)){
			appId = param.get(Constants.REQUEST_HEADER_APPID);
		}else{
			throw new AuthenticationException();
		}
		if(param.containsKey(Constants.REQUEST_HEADER_APPUUID)){
			appUUID = param.get(Constants.REQUEST_HEADER_APPUUID);
		}else{
			throw new AuthenticationException();
		}
		if(param.containsKey(Constants.REQUEST_HEADER_TIMESTAMP)){
			timestamp = param.get(Constants.REQUEST_HEADER_TIMESTAMP);
		}else{
			throw new AuthenticationException();
		}

		String secret = shiroService.getAppSecretByAppId(appId);
		if(StringUtils.isEmpty(secret)){
			throw new AuthenticationException();
		}

		// 对param进行字典排序
		param = ShiroRequestUtil.getSortedParam(param);

		// 将 appId & appUUID & timestamp 加入到redis进行校验
		if(!shiroService.compareAndSetAppTimestamp(appId, appUUID, timestamp)){
			throw new AuthenticationException();
		}

		// 从服务器获取对应的 secret / appId appUUID timestamp 进行加密
		String serverUserSign = HmacSHA256Utils.digest(secret, param);

		// 然后进行客户端消息摘要和服务器端消息摘要的匹配
		return new SimpleAuthenticationInfo(appId, serverUserSign, getName());
	}


	/**
	 * 检查此账号是否可用
	 */
	private void checkAccountState(Admin admin) {
		if(admin.getAccountEnabled()!=1){
			throw new DisabledAccountException(); //账号已被禁用
		}
	}
}
